package com.flyqiu.common.clazz;

import com.flyqiu.common.crypto.md5.FlyQiuMd5;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 工具类，用于操作类和字段。
 */
public class FlyQiuClassTools {

    private static final Map<String, Method> methodsMap = new HashMap<>();

    /**
     * 获取类的所有属性，包括父类。
     *
     * @param object 对象实例
     * @return 所有字段列表
     */
    public static List<Field> getAllFields(Object object) {
        Class<?> clazz = object.getClass();
        return getAllFields(clazz);
    }

    /**
     * 获取类的所有属性，包括父类。如果子类包含与父类相同的属性名称，则以子类为准。
     *
     * @param clazz 类对象
     * @return 所有字段列表
     */
    public static List<Field> getAllFields(Class<?> clazz) {
        Map<String, Field> fieldList = new HashMap<>();
        while (clazz != null) {
            Map<String, Field> fieldMap = Arrays.stream(clazz.getDeclaredFields())
                    .filter(e -> !fieldList.containsKey(e.getName()))
                    .collect(Collectors.toMap(Field::getName, e -> e));
            fieldList.putAll(fieldMap);
            clazz = clazz.getSuperclass();
        }
        return new ArrayList<>(fieldList.values());
    }

    private static final Function<Field, String> DEF_FIELD_KEY_MAPPER = Field::getName;
    private static final Function<Field, Field> DEF_FIELD_VALUE_MAPPER = (field -> field);

    /**
     * 获取所有字段，并应用过滤器。
     *
     * @param clazz  类对象
     * @param filter 字段过滤器
     * @return 过滤后的字段列表
     */
    public static List<Field> getAllFields(Class<?> clazz, FlyQiuFieldFilter filter) {
        Map<String, Field> allFieldMap = getAllFieldMap(clazz, filter, DEF_FIELD_KEY_MAPPER, DEF_FIELD_VALUE_MAPPER);
        return new ArrayList<>(allFieldMap.values());
    }

    /**
     * 获取所有字段，并应用过滤器和值映射函数。
     *
     * @param clazz     类对象
     * @param filter    字段过滤器
     * @param valMapper 值映射函数
     * @param <R>       返回类型
     * @return 过滤后的字段列表
     */
    public static <R> List<R> getAllFields(Class<?> clazz, FlyQiuFieldFilter filter, Function<Field, R> valMapper) {
        Map<String, R> allFieldMap = getAllFieldMap(clazz, filter, DEF_FIELD_KEY_MAPPER, valMapper);
        return new ArrayList<>(allFieldMap.values());
    }

    /**
     * 获取所有字段，并应用键值映射函数。
     *
     * @param clazz     类对象
     * @param filter    字段过滤器
     * @param valMapper 值映射函数
     * @param <V>       返回类型
     * @return 过滤后的字段映射
     */
    public static <V> Map<String, V> getAllFieldMap(Class<?> clazz, FlyQiuFieldFilter filter, Function<Field, V> valMapper) {
        return getAllFieldMap(clazz, filter, DEF_FIELD_KEY_MAPPER, valMapper);
    }

    /**
     * 获取所有字段，并应用键值映射函数。
     *
     * @param clazz     类对象
     * @param filter    字段过滤器
     * @param keyMapper 键映射函数
     * @param valMapper 值映射函数
     * @param <K>       键类型
     * @param <V>       值类型
     * @return 过滤后的字段映射
     */
    public static <K, V> Map<K, V> getAllFieldMap(Class<?> clazz, FlyQiuFieldFilter filter, Function<Field, K> keyMapper, Function<Field, V> valMapper) {
        Map<K, V> result = new LinkedHashMap<>();
        while (clazz != null) {
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                K key = keyMapper.apply(field);
                if (result.containsKey(key)) {
                    continue;
                }
                if (filter.filter(field, clazz)) {
                    result.put(key, valMapper.apply(field));
                }
            }
            clazz = clazz.getSuperclass();
        }
        return result;
    }

    /**
     * 获取有效的字段（排除静态和最终字段）。
     *
     * @param object 对象实例
     * @return 有效字段列表
     */
    public static List<Field> getEffectiveFields(Object object) {
        return getEffectiveFields(object.getClass());
    }

    /**
     * 获取有效的字段（排除静态和最终字段）。
     *
     * @param clazz 类对象
     * @return 有效字段列表
     */
    public static List<Field> getEffectiveFields(Class<?> clazz) {
        List<Field> allFields = getAllFields(clazz);
        return allFields.stream()
                .filter(field -> !Modifier.isFinal(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()))
                .collect(Collectors.toList());
    }

    /**
     * 获取类的所有公共方法。
     *
     * @param clazz 类对象
     * @return 公共方法列表
     */
    public static List<Method> getClassAllPublicMethod(Class<?> clazz) {
        List<Method> methods = new ArrayList<>();
        findClassAllPublicMethod(clazz, methods::add);
        return methods;
    }

    /**
     * 查找类的所有公共方法。
     *
     * @param clazz          类对象
     * @param methodConsumer 方法消费者
     */
    public static void findClassAllPublicMethod(Class<?> clazz, Function<Method, Boolean> methodConsumer) {
        if (clazz.equals(Object.class)) {
            return;
        }
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if (methodConsumer.apply(method)) {
                return;
            }
        }
        Class<?> superclass = clazz.getSuperclass();
        if (superclass != null) {
            findClassAllPublicMethod(superclass, methodConsumer);
        }
    }

    /**
     * 获取对象类型的数组。
     *
     * @param params 参数数组
     * @return 类型数组
     */
    public static Class<?>[] getObjTypes(Object... params) {
        Class<?>[] clazzTypes = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            clazzTypes[i] = params[i].getClass();
        }
        return clazzTypes;
    }

    /**
     * 查找公共方法。
     *
     * @param clazz      类对象
     * @param methodName 方法名
     * @param params     参数
     * @return 方法对象
     */
    public static Method findPublicMethod(Class<?> clazz, String methodName, Object... params) {
        Class<?>[] clazzTypes = getObjTypes(params);
        return findPublicMethod(clazz, methodName, clazzTypes);
    }

    /**
     * 查找公共方法。
     *
     * @param clazz       类对象
     * @param methodName  方法名
     * @param paramsTypes 参数类型
     * @return 方法对象
     */
    public static Method findPublicMethod(Class<?> clazz, String methodName, Class<?>... paramsTypes) {
        if (clazz == null || clazz == Object.class) {
            return null;
        }
        StringBuilder keyBuilder = new StringBuilder(clazz.getName());
        keyBuilder.append(methodName);
        String paramsKey = Arrays.stream(paramsTypes)
                .map(Class::getName)
                .collect(Collectors.joining());
        keyBuilder.append(paramsKey);
        String key = FlyQiuMd5.md5(keyBuilder.toString());
        if (methodsMap.containsKey(key)) {
            return methodsMap.get(key);
        }
        Method method = null;
        do {
            method = getPublicMethod(clazz, methodName, paramsTypes);
            if (method == null) {
                clazz = clazz.getSuperclass();
                continue;
            }
            method.setAccessible(true);
            methodsMap.put(key, method);
            return method;
        } while (clazz != null && clazz != Object.class);
        return method;
    }

    /**
     * 获取公共方法。
     *
     * @param clazz       类对象
     * @param methodName  方法名
     * @param paramsTypes 参数类型
     * @return 方法对象
     */
    public static Method getPublicMethod(Class<?> clazz, String methodName, Class<?>... paramsTypes) {
        try {
            return clazz.getMethod(methodName, paramsTypes);
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    /**
     * 调用方法。
     *
     * @param method 方法对象
     * @param object 对象实例
     * @param params 参数
     * @return 方法调用结果
     */
    public static Object methodInvoke(Method method, Object object, Object... params) {
        if (method == null) {
            throw new IllegalArgumentException("Method cannot be null");
        }
        if (object == null) {
            throw new IllegalArgumentException("Method invoke object cannot be null");
        }
        try {
            if (!method.canAccess(object)) {
                method.setAccessible(true);
            }
            return method.invoke(object, params);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 加载类。
     *
     * @param className 类名
     * @return 类对象
     */
    public static Class<?> loadClass(String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 判断类型是否继承某个类型或实现某个接口。
     *
     * @param childClazz  子类
     * @param parentClazz 父类或接口
     * @return 是否继承或实现
     */
    public static boolean isForm(Class<?> childClazz, Class<?> parentClazz) {
        return parentClazz.isAssignableFrom(childClazz);
    }

    /**
     * 判断一个类型不是另一个类型的子类或实现类。
     *
     * @param childClazz  子类
     * @param parentClazz 父类或接口
     * @return 是否不是子类或实现类
     */
    public static boolean isNotForm(Class<?> childClazz, Class<?> parentClazz) {
        return !isForm(childClazz, parentClazz);
    }

    /**
     * 获取指定名称的非静态非最终成员变量。
     *
     * @param fieldName 字段名
     * @param o         对象实例
     * @return 匹配的字段列表
     */
    public static List<Field> getTargetField(String fieldName, Object o) {
        List<Field> allFields = getAllFields(o);
        return allFields.stream()
                .filter(field -> field.getName().equals(fieldName) && !Modifier.isFinal(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()))
                .collect(Collectors.toList());
    }

    /**
     * 创建对象实例。
     *
     * @param clazz 类对象
     * @param <T>   泛型类型
     * @return 新创建的对象实例
     */
    public static <T> T createObject(Class<T> clazz) {
        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
}
